/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.blockly.ohos.demo;

import com.google.blockly.ohos.BlocklySectionsAbility;
import com.google.blockly.ohos.codegen.CodeGenerationRequest;
import com.google.blockly.ohos.control.BlocklyController;
import com.google.blockly.model.DefaultBlocks;
import com.google.blockly.utils.JavascriptUtil;
import com.google.blockly.utils.LogUtils;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.webengine.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextTool;
import ohos.agp.window.dialog.PopupDialog;
import ohos.agp.window.dialog.ToastDialog;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.Resource;
import ohos.utils.net.Uri;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.List;


/**
 * Demo app with the Blockly Games turtle game in a webview.
 */
public class TurtleAbility extends BlocklySectionsAbility {
    private static final String TAG = "TurtleAbility";

    private static final String SAVE_FILENAME = "turtle_workspace.xml";
    private static final String AUTOSAVE_FILENAME = "turtle_workspace_temp.xml";

    private String[] mLevelNames;

    private String filename = "";

    static final List<String> TURTLE_BLOCK_DEFINITIONS = Arrays.asList(
            DefaultBlocks.COLOR_BLOCKS_PATH,
            DefaultBlocks.LOGIC_BLOCKS_PATH,
            DefaultBlocks.LOOP_BLOCKS_PATH,
            DefaultBlocks.MATH_BLOCKS_PATH,
            DefaultBlocks.TEXT_BLOCKS_PATH,
            DefaultBlocks.VARIABLE_BLOCKS_PATH,
            "turtle/turtle_blocks.json"
    );
    static final List<String> TURTLE_BLOCK_GENERATORS = Arrays.asList(
            "turtle/generators.js"
    );
    private static final int MAX_LEVELS = 10;
    private static final String[] LEVEL_TOOLBOX = new String[MAX_LEVELS];

    static {
        LEVEL_TOOLBOX[0] = "toolbox_basic.xml";
        LEVEL_TOOLBOX[1] = "toolbox_basic.xml";
        LEVEL_TOOLBOX[2] = "toolbox_colour.xml";
        LEVEL_TOOLBOX[3] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[4] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[5] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[6] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[7] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[8] = "toolbox_colour_pen.xml";
        LEVEL_TOOLBOX[9] = "toolbox_advanced.xml";
    }

    private final EventHandler mHandler = new EventHandler(EventRunner.getMainEventRunner());
    private Text mTitle;
    private WebView mTurtleWebview;
    private final CodeGenerationRequest.CodeGeneratorCallback mCodeGeneratorCallback =
            new CodeGenerationRequest.CodeGeneratorCallback() {
                @Override
                public void onFinishCodeGeneration(String generatedCode) {
                    // Sample callback.
                    LogUtils.i(TAG, "generatedCode: " + generatedCode);
                    if (TextTool.isNullOrEmpty(generatedCode)) {
                        generatedCode = getCode();
                    }
                    String finalGeneratedCode = generatedCode;

                    Text text = new Text(TurtleAbility.this);
                    text.setMultipleLine(true);
                    text.setTextSize(AttrHelper.fp2px(12, TurtleAbility.this));
                    text.setText(generatedCode);
                    new ToastDialog(TurtleAbility.this)
                            .setComponent(text)
                            .setAlignment(LayoutAlignment.CENTER)
                            .setDuration(3000)
                            .show();

                    mHandler.postTask(() -> {
                        String encoded = "Turtle.execute('"
                                + JavascriptUtil.makeJsString(finalGeneratedCode) + "')";
                        mTurtleWebview.executeJs("javascript:" + encoded, null);
                    });
                }
            };

    public String getCode() {
        String xmlCodeName = "ohos_code.xml";
        if (filename.contains("lacey_curves")) {
            xmlCodeName = "lacey_curves_code.xml";
        } else if (filename.contains("paint_strokes")) {
            xmlCodeName = "paint_strokes_code.xml";
        }
        BufferedReader reader = null;
        try {
            Resource ohosCodeResource = getResourceManager()
                    .getRawFileEntry("entry/resources/rawfile/turtle/demo_workspaces/" + xmlCodeName).openRawFile();
            reader = new BufferedReader(new InputStreamReader(ohosCodeResource, "utf-8"));
            StringBuilder ohosCodeString = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                ohosCodeString.append(line);
                ohosCodeString.append('\n');
            }
            return ohosCodeString.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "";
    }

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        mTitle = (Text) findComponentById(com.google.blockly.ohos.ResourceTable.Id_title);
        mTitle.setText(mLevelNames[0]);
        mTurtleWebview = (WebView) findComponentById(ResourceTable.Id_turtle_runtime);
        mTurtleWebview.getWebConfig().setJavaScriptPermit(true);
        mTurtleWebview.setBrowserAgent(new BrowserAgent(this));
        mTurtleWebview.setWebAgent(new WebAgent() {
            @Override
            public ResourceResponse processResourceRequest(WebView webview, ResourceRequest request) {
                final String authority = "example.com";
                final String rawFile = "/rawfile/";
                Uri requestUri = request.getRequestUrl();
                if (authority.equals(requestUri.getDecodedAuthority())) {
                    String path = requestUri.getDecodedPath();
                    if (TextTool.isNullOrEmpty(path)) {
                        return super.processResourceRequest(webview, request);
                    }
                    if (path.startsWith(rawFile)) {
                        String rawFilePath = "entry/resources/rawfile/" + path.replace(rawFile, "");
                        String mimeType = URLConnection.guessContentTypeFromName(rawFilePath);
                        try {
                            Resource resource = getResourceManager().getRawFileEntry(rawFilePath).openRawFile();
                            ResourceResponse response = new ResourceResponse(mimeType, resource, null);
                            return response;
                        } catch (IOException e) {
                            LogUtils.e(TAG, "open raw file failed");
                        }
                    }
                }
                return super.processResourceRequest(webview, request);
            }
        });
        mTurtleWebview.load("https://example.com/rawfile/turtle/turtle.html");
    }

    @Override
    public int getBlocklyLayoutRes() {
        return ResourceTable.Layout_turtle_content;
    }

    @Override
    public void onLoadWorkspace() {
        mBlocklyAbilityHelper.loadWorkspaceFromAppDirSafely(SAVE_FILENAME);
    }

    @Override
    public void onSaveWorkspace() {
        mBlocklyAbilityHelper.saveWorkspaceToAppDirSafely(SAVE_FILENAME);
    }

    @Override
    public void onClick(Component component) {
        super.onClick(component);
        onItemClicked(component.getId());
    }

    private void onItemClicked(int id) {
        if (id == ResourceTable.Id_action_demo_ohos) {
            filename = "ohos.xml";
        } else if (id == ResourceTable.Id_action_demo_lacey_curves) {
            filename = "lacey_curves.xml";
        } else if (id == ResourceTable.Id_action_demo_paint_strokes) {
            filename = "paint_strokes.xml";
        } else {
            return;
        }
        mMenuDialog.hide();

        String assetFilename = "turtle/demo_workspaces/" + filename;
        BlocklyController controller = getController();
        try {
            controller.loadWorkspaceContents(getResourceManager()
                    .getRawFileEntry("entry/resources/rawfile/" + assetFilename).openRawFile());
        } catch (Exception e) {
            LogUtils.e(TAG, "Failed to load workspace");
        }
        addDefaultVariables(controller);
    }

    public void showMenuComponent() {
        if (mMenuDialog == null) {
            Component menu = LayoutScatter.getInstance(getContext()).parse(getDefaultMenuLayoutRes(), null, false);
            menu.findComponentById(ResourceTable.Id_action_clear).setClickedListener(this);
            menu.findComponentById(ResourceTable.Id_action_run).setClickedListener(this);
            menu.findComponentById(ResourceTable.Id_action_demo_ohos).setClickedListener(this);
            menu.findComponentById(ResourceTable.Id_action_demo_lacey_curves).setClickedListener(this);
            menu.findComponentById(ResourceTable.Id_action_demo_paint_strokes).setClickedListener(this);
            mMenuDialog = new PopupDialog(getContext(), findComponentById(com.google.blockly.ohos.ResourceTable.Id_action_more));
            mMenuDialog.setCustomComponent(menu);
            mMenuDialog.setDialogListener(() -> {
                mMenuDialog.hide();
                return false;
            });
        }
        if (!mMenuDialog.isShowing()) {
            mMenuDialog.show();
        }
    }

    public int getDefaultMenuLayoutRes() {
        return ResourceTable.Layout_turtle_menu;
    }

    @Override
    protected List<String> getBlockDefinitionsJsonPaths() {
        // Use the same blocks for all the levels. This lets the user's block code carry over from
        // level to level. The set of blocks shown in the toolbox for each level is defined by the
        // toolbox path below.
        return TURTLE_BLOCK_DEFINITIONS;
    }

    @Override
    protected List<String> getGeneratorsJsPaths() {
        return TURTLE_BLOCK_GENERATORS;
    }

    @Override
    protected String getToolboxContentsXmlPath() {
        // Expose a different set of blocks to the user at each level.
        return "turtle/" + LEVEL_TOOLBOX[getCurrentSectionIndex()];
    }

    @Override
    protected void onInitBlankWorkspace() {
        addDefaultVariables(getController());
    }

    @Override
    protected BaseItemProvider onCreateSectionsListAdapter() {
        // Create the game levels with the labels "Level 1", "Level 2", etc., displaying
        // them as simple text items in the sections drawer.
        mLevelNames = new String[MAX_LEVELS];
        for (int i = 0; i < MAX_LEVELS; ++i) {
            mLevelNames[i] = "Level " + (i + 1);
        }
        return new Adapter();
    }

    private class Adapter extends BaseItemProvider {

        @Override
        public int getCount() {
            return mLevelNames.length;
        }

        @Override
        public Object getItem(int i) {
            return mLevelNames[i];
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
            ViewHolder viewHolder;
            if (component == null) {
                component = LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_sections_item, null, false);
                viewHolder = new ViewHolder();
                viewHolder.itemView = (Text) component.findComponentById(ResourceTable.Id_text_item);
                component.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) component.getTag();
            }
            viewHolder.itemView.setText(mLevelNames[i]);
            ShapeElement shapeElement = new ShapeElement();
            shapeElement.setRgbColor(RgbColor.fromArgbInt(
                    getCurrentSectionIndex() == i ? Color.MAGENTA.getValue() : Color.TRANSPARENT.getValue()));
            viewHolder.itemView.setBackground(shapeElement);
            return component;
        }

        private class ViewHolder {
            public Text itemView;
        }

    }

    @Override
    protected boolean onSectionChanged(int oldSection, int newSection) {
        mTitle.setText(mLevelNames[newSection]);
        reloadToolbox();
        return true;
    }

    @Override
    protected CodeGenerationRequest.CodeGeneratorCallback getCodeGenerationCallback() {
        return mCodeGeneratorCallback;
    }

    static void addDefaultVariables(BlocklyController controller) {
        // TODO: (#22) Remove this override when variables are supported properly
        controller.addVariable("item");
        controller.addVariable("count");
        controller.addVariable("marshmallow");
        controller.addVariable("lollipop");
        controller.addVariable("kitkat");
        controller.addVariable("ohos");
    }

    /**
     * Optional override of the save path, since this demo Ability has multiple Blockly
     * configurations.
     *
     * @return Workspace save path used by this Ability.
     */
    @Override
    protected String getWorkspaceSavePath() {
        return SAVE_FILENAME;
    }

    /**
     * Optional override of the auto-save path, since this demo Ability has multiple Blockly
     * configurations.
     *
     * @return Workspace auto-save path used by this Ability.
     */
    @Override
    protected String getWorkspaceAutosavePath() {
        return AUTOSAVE_FILENAME;
    }
}
